import os
import ctypes
import logging

PATH_NUT = os.environ["NUTHOME"]

def load_cdll(rpath_from_nut):
    path_libplc = PATH_NUT + rpath_from_nut
    try:
        lib_cdll = ctypes.CDLL(path_libplc)
        return lib_cdll
    except KeyError:
        # raise Exception("未发现NUTHOME目录及PLC相关lib库")
        return False


class IO_Slave:
    singleton_step = [False, False]

    def __new__(cls, *args, **kargs):
        if not cls.singleton_step[0]:
            cls.singleton_instance = super().__new__(cls)
            cls.singleton_step[0] = True
        return cls.singleton_instance

    def __init__(self):
        if self.singleton_step[1]:  return

        super().__init__()
        self.singleton_step[1] = True

        self.lib_plc = load_cdll("/lib/libnutaccess.so")
        if self.lib_plc and not self.lib_plc.PycGetM():
            logging.error("无法初始化PLC数据信息")
            # del self.lib_plc
            self.lib_plc = False
        if self.lib_plc:
            logging.error("无法连接到Panel-IO数据信息")
            self.lib_panel = False if self.lib_plc.PycGetIOShm() < 0 else self.lib_plc

    def get_plcmb_pin_value(self, address):
        """ return bool of PLC.MB pin status """
        addr_byte, addr_bit = [int(x) for x in address.split(".")]
        if self.lib_plc:
            return self.lib_plc.PycGetMBit(addr_byte, addr_bit)

    def set_plcmb_pin_value(self, address, forced_value):
        addr_byte, addr_bit = [int(x) for x in address.split(".")]
        if self.lib_plc:
            self.lib_plc.PycSetMBit(addr_byte, addr_bit, forced_value)

    def get_panel_pin_value(self, address, isInput):
        """ return number of IO pin status """
        addr_byte, addr_bit = [int(x) for x in address.split(".")]
        if self.lib_panel:
            return self.lib_panel.PycGetIoValueByBit(isInput, addr_byte, addr_bit)


from data.iopin import IO_Pin
IO_VALUE = IO_Slave()

def get_pin_value(str_addr):  # 仅适用于PLC/PANEL引脚
    io_pin = IO_Pin(str_addr)
    if io_pin.maintype == "PLC":
        return str(IO_VALUE.get_plcmb_pin_value(io_pin.address))
    elif io_pin.maintype == "PANEL":  # no check??
        return str(IO_VALUE.get_panel_pin_value(io_pin.address, True if io_pin.subtype == "IX" else False))
    else:
        # return False
        raise Exception("无法求值的格式数据项")

def get_pin_forced_value(str_addr):
    io_pin = IO_Pin(str_addr)
    if io_pin.maintype == "PLC":
        return str(IO_VALUE.get_plcmb_pin_value(io_pin.address))
    elif io_pin.maintype == "PANEL":
        return str(IO_VALUE.get_panel_pin_value(io_pin.address, True if io_pin.subtype == "IX" else False))
    else:
        # return False
        raise Exception("无法求值的格式数据项")
